# -*- coding: utf-8 -*-
"""
集中式路由发现与注册

约定：
- 仅扫描 `app.api.v1` 包内，顶级目录以 `module_` 开头的模块。
- 在各模块任意子目录下的 `controller.py` 中定义的 `APIRouter` 实例会自动被注册。
- 顶级目录 `module_xxx` 会映射为容器路由前缀 `/<xxx>`。

设计目标：
- 稳定、可预测：有序扫描与注册，确定性日志输出。
- 简洁、易维护：职责拆分成小函数，类型提示与清晰注释。
- 安全、可控：去重处理、异常分层记录、可配置的前缀映射与忽略规则。
"""

from __future__ import annotations

import importlib
from pathlib import Path
from typing import Dict, Iterable, Optional, Set, Tuple

from fastapi import APIRouter

from app.core.logger import log

# ----- 约定与配置 -----
MODULE_PREFIX = "module_"

# 可选：前缀映射与忽略规则（需要时可在此处进行配置）
PREFIX_MAP: Dict[str, str] = {
    # e.g. "module_application": "/application",
}
EXCLUDE_DIRS: Set[str] = set()
EXCLUDE_FILES: Set[str] = set()

# 创建根路由（对外暴露）
router = APIRouter()

# 记录已注册的 APIRouter，避免重复 include（既包含控制器路由，也包含容器路由）
_seen_router_ids: Set[int] = set()


# ----- 工具函数 -----
def _get_v1_base_dir_and_pkg() -> Tuple[Path, str]:
    """定位 `app.api.v1` 包的文件系统路径与包名。

    返回:
    - (Path, str): (`app/api/v1` 的路径, 包名 `app.api.v1`)
    """
    try:
        v1_pkg = importlib.import_module("app.api.v1")
        base_dir = Path(next(iter(v1_pkg.__path__)))
        base_pkg = v1_pkg.__name__
        return base_dir, base_pkg
    except Exception as e:
        log.error(f"❌️ 定位 app.api.v1 失败: {e}")
        raise


def _iter_controller_files(base_dir: Path) -> Iterable[Path]:
    """递归查找并返回所有 `controller.py` 文件，按路径排序保证确定性。"""
    return sorted(base_dir.rglob("controller.py"), key=lambda p: p.as_posix())


def _resolve_prefix(top_module: str) -> Optional[str]:
    """将顶级模块目录名解析为容器前缀。

    - 过滤被忽略的目录。
    - 仅处理以 `module_` 开头的目录。
    - 支持通过 `PREFIX_MAP` 自定义映射，否则默认 `/<xxx>`。
    """
    if top_module in EXCLUDE_DIRS:
        return None
    if not top_module.startswith(MODULE_PREFIX):
        return None
    mapped = PREFIX_MAP.get(top_module)
    return mapped or f"/{top_module[len(MODULE_PREFIX):]}"


def _include_module_routers(mod: object, container: APIRouter) -> int:
    """将模块中的所有 `APIRouter` 实例包含到指定容器路由中。

    返回:
    - int: 新增注册的路由数量
    """
    from fastapi import APIRouter as _APIRouter

    added = 0
    for attr_name in dir(mod):
        attr = getattr(mod, attr_name, None)
        if isinstance(attr, _APIRouter):
            rid = id(attr)
            if rid in _seen_router_ids:
                continue
            _seen_router_ids.add(rid)
            container.include_router(attr)
            added += 1
    return added


# ----- 主流程 -----
def _discover_and_register() -> None:
    """通过文件系统递归扫描并注册路由到根路由。"""
    base_dir, base_pkg = _get_v1_base_dir_and_pkg()
    containers: Dict[str, APIRouter] = {}
    container_counts: Dict[str, int] = {}

    scanned_files = 0
    imported_modules = 0
    included_routers = 0

    for file in _iter_controller_files(base_dir):
        rel_path = file.relative_to(base_dir).as_posix()
        scanned_files += 1

        if rel_path in EXCLUDE_FILES:
            continue

        parts = file.relative_to(base_dir).parts
        if len(parts) < 2:
            # 至少应包含顶级目录和文件名（如 module_xxx/.../controller.py）
            continue

        top_module = parts[0]
        prefix = _resolve_prefix(top_module)
        if not prefix:
            # 非约定模块或被忽略
            continue

        # 拼接模块导入路径: app.api.v1.<...>.controller
        mod_path = ".".join((base_pkg,) + tuple(parts[:-1]) + ("controller",))
        try:
            mod = importlib.import_module(mod_path)
            imported_modules += 1
        except ModuleNotFoundError:
            log.error(f"❌️ 未找到控制器模块: {mod_path}")
            continue
        except Exception as e:
            log.error(f"❌️ 导入控制器失败: {mod_path} -> {e}")
            continue

        container = containers.setdefault(prefix, APIRouter(prefix=prefix))
        try:
            added = _include_module_routers(mod, container)
            included_routers += added
            container_counts[prefix] = container_counts.get(prefix, 0) + added
        except Exception as e:
            log.error(f"❌️ 注册控制器路由失败: {mod_path} -> {e}")

    # 将容器路由按前缀名称排序后注册到根路由，保证顺序稳定
    for prefix in sorted(containers.keys()):
        container = containers[prefix]
        rid = id(container)
        if rid in _seen_router_ids:
            continue
        _seen_router_ids.add(rid)
        router.include_router(container)
        # 更丰富的注册日志（含路由数量）
        log.info(f"✅️ 已注册模块容器: {prefix}")

    log.info(
        (
            f"✅️ 路由发现完成: 扫描文件 {scanned_files}, "
            f"导入模块 {imported_modules}, 注册路由 {included_routers}, "
            f"容器 {len(containers)}"
        )
    )


# 执行自动发现注册（模块导入即生效）
_discover_and_register()